package com.zoe.onelink.business.service;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.zoe.onelink.business.cache.bean.IParamEntity;
import com.zoe.onelink.business.cache.bean.ParamEntity;
import com.zoe.onelink.business.constant.CacheConst;
import com.zoe.onelink.business.constant.RequestConst;
import com.zoe.onelink.business.dao.ParamDao;
import com.zoe.onelink.business.property.OnelinkBizProperties;
import com.zoe.onelink.util.OnelinkContextHolder;
import com.zoe.onelink.util.StrUtil;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.ApplicationContext;

import java.time.Duration;

/**
 * <p>标题: </p>
 * <p>描述: </p>
 * <p>版权: Copyright (c) 2019</p>
 * <p>公司: </p>
 *
 * @version: 1.0
 * @author: lanzhe
 * @date: 2019/11/28
 */
@Slf4j
public class ParamService implements SmartInitializingSingleton {

    private final ApplicationContext applicationContext;
    private final Cache<String, IParamEntity> localCache;

    private ParamDao paramDao;
    private ParamService paramService;

    private final OnelinkBizProperties.BizParam param;

    public ParamService(ApplicationContext applicationContext, OnelinkBizProperties.BizParam param) {
        this.applicationContext = applicationContext;
        this.param = param;
        this.localCache = Caffeine.newBuilder()
                .expireAfterWrite(Duration.ofMinutes(1))
                .maximumSize(1024)
                .softValues().build();
    }

    @Override
    public void afterSingletonsInstantiated() {
        this.paramDao = this.applicationContext.getBean(ParamDao.class);
        this.paramService = this.applicationContext.getBean(ParamService.class);
    }

    /**
     * 获取用户参数值
     */
    public ParamEntity getUserParam(String paramName) {
        return this.paramService.getUserParam(paramName, OnelinkContextHolder.getString(RequestConst.HEADER_OPERATOR), false);
    }

    /**
     * 获取用户参数值
     */
    @Cacheable(cacheNames = CacheConst.STAFF_TYPE_PARAMS_PRE + "#30m", key = "#paramName + ':' + #operator",
            condition = "#useCache && T(com.zoe.onelink.util.StrUtil).isAllNotBlank(#paramName,#operator)", unless = "#result == null")
    public ParamEntity getUserParam(String paramName, String operator, boolean useCache) {
        if (StrUtil.hasBlank(paramName, operator)) {
            return null;
        }
        return paramDao.getCustomParam(CacheConst.USER_PARAMS_NAME, operator, paramName);
    }

    /**
     * 获取科室参数值
     */
    public ParamEntity getDeptParam(String paramName) {
        return this.paramService.getDeptParam(paramName, OnelinkContextHolder.getString(RequestConst.HEADER_DEPT_CODE), false);
    }

    @Cacheable(cacheNames = CacheConst.DEPT_TYPE_PARAMS_PRE + "#30m", key = "#paramName + ':' + #deptCode",
            condition = "#useCache && T(com.zoe.onelink.util.StrUtil).isAllNotBlank(#paramName,#deptCode)",
            unless = "#result == null")
    public ParamEntity getDeptParam(String paramName, String deptCode, boolean useCache) {
        if (StrUtil.hasBlank(paramName, deptCode)) {
            return null;
        }
        return paramDao.getCustomParam(CacheConst.DEPT_PARAMS_NAME, deptCode, paramName);
    }


    /**
     * 获取院区参数值
     */
    public ParamEntity getDistrictParam(String paramName) {
        return this.paramService.getDistrictParam(paramName, OnelinkContextHolder.getString(RequestConst.HEADER_DISTRICT_CODE), false);
    }

    /**
     * 获取院区参数值
     */
    @Cacheable(cacheNames = CacheConst.DISTRICT_TYPE_PARAMS_PRE + "#30m", key = "#paramName + ':' + #districtCode",
            condition = "#useCache && T(com.zoe.onelink.util.StrUtil).isAllNotBlank(#paramName,#districtCode)",
            unless = "#result == null")
    public ParamEntity getDistrictParam(String paramName, String districtCode, boolean useCache) {
        if (StrUtil.hasBlank(paramName, districtCode)) {
            return null;
        }
        return paramDao.getCustomParam(CacheConst.DISTRICT_PARAMS_NAME, districtCode, paramName);
    }

    /**
     * 获取全局业务参数配置
     *
     * @param paramName 参数名
     */
    @Cacheable(cacheNames = CacheConst.GLOBAL_TYPE_PARAMS_PRE + "#30m", key = "#paramName",
            condition = "#useCache && #paramName != null", unless = "#result == null")
    public ParamEntity getGlobalParam(String paramName, boolean useCache) {
        return paramDao.getGlobalParam(paramName);
    }


    /**
     * 获取系统参数值 (旧的业务逻辑)
     */
    public IParamEntity getBizParamValue(@NonNull String paramName) {
        //  用户参数
        String operator = OnelinkContextHolder.getString(RequestConst.HEADER_OPERATOR);
        ParamEntity userParam = this.paramService.getUserParam(paramName, operator, true);
        if (userParam != null) {
            return userParam;
        }
        // 科室参数
        String deptCode = OnelinkContextHolder.getString(RequestConst.HEADER_DEPT_CODE);
        ParamEntity deptParam = this.paramService.getDeptParam(paramName, deptCode, true);
        if (deptParam != null) {
            return deptParam;
        }
        // 院区参数
        String districtCode = OnelinkContextHolder.getString(RequestConst.HEADER_DISTRICT_CODE);
        ParamEntity districtParam = this.paramService.getDistrictParam(paramName, districtCode, true);
        if (districtParam != null) {
            return districtParam;
        }
        // 全局配置
        return this.paramService.getGlobalParam(paramName, true);
    }

    public IParamEntity getBizParamValueInDB(@NonNull String paramName) {
        return this.getBizParamValueInDB(paramName, this.param.isLocalCacheEnabled());
    }

    /**
     * 从数据库直接获取
     */
    public IParamEntity getBizParamValueInDB(@NonNull String paramName, boolean useLocalCache) {
        String operator = OnelinkContextHolder.getString(RequestConst.HEADER_OPERATOR);
        String deptCode = OnelinkContextHolder.getString(RequestConst.HEADER_DEPT_CODE);
        String districtCode = OnelinkContextHolder.getString(RequestConst.HEADER_DISTRICT_CODE);
        IParamEntity param;
        String key = paramName.toUpperCase() + "#" + StrUtil.nullToEmpty(operator) + ":" + StrUtil.nullToEmpty(deptCode) + ":" + StrUtil.nullToEmpty(districtCode);
        // 使用本地缓存且业务参数不在排除列表中
        if (useLocalCache && !this.param.getLocalCacheExcludeList().contains(paramName)) {
            param = this.localCache.get(key, k -> this.paramDao.getBizParam(paramName, operator, deptCode, districtCode));
        } else {
            param = this.paramDao.getBizParam(paramName, operator, deptCode, districtCode);
        }
        return param;
    }

    public void clearLocalCache() {
        log.info("[BizParam] 清空本地缓存");
        this.localCache.invalidateAll();
    }

}
